﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Xml.Linq;
using Microsoft.SharePoint;
using SPQueryToSql.ApplicationObjects.CAML;
using FieldInfo = SPQueryToSql.ApplicationObjects.FieldInfo;

namespace SPQueryToSql.Extensions
{
    public static class SPListExtensions
    {
        #region Count

        ///<summary>
        /// Count the number of item in the default list view
        ///</summary>
        ///<param name="list">Target SharePoint List</param>
        ///<returns>Number of the item</returns>
        public static int Count(this SPList list)
        {
            return list.Count(list.DefaultView);
        }

        /// <summary>
        /// Count the number of item in the specific query
        /// </summary>
        /// <param name="list">Target SharePoint List</param>
        /// <param name="spQuery">SPQuery object used to retrieve data</param>
        /// <returns>Number of the item</returns>
        public static int Count(this SPList list, SPQuery spQuery)
        {
            var query = new Query(spQuery, list);
            return query.Count();
        }

        /// <summary>
        /// Count the number of item in the specific list view
        /// </summary>
        /// <param name="list">Target SharePoint List</param>
        /// <param name="view">Specific list view</param>
        /// <returns>Number of the item</returns>
        public static int Count(this SPList list, SPView view)
        {
            var queryStr = string.Format("<View>{0}{1}<QueryOptions><ViewAttributes Scope=\"{2}\" /><RowLimit>{3}</RowLimit></QueryOptions></View>",
                                         view.ViewFields.SchemaXml, view.Query, view.Scope, view.RowLimit);
            var query = new Query(queryStr, list);
            return query.Count();
        }

        #endregion

        #region Any

        /// <summary>
        /// Determines whether the default list view contains any item.
        /// </summary>
        /// <param name="list">Target SharePoint List</param>
        /// <returns>true if the default list view contains any item; otherwise, false.</returns>
        public static bool Any(this SPList list)
        {
            return list.Any(list.DefaultView);
        }

        /// <summary>
        /// Determines whether the resultset of query contains any item.
        /// </summary>
        /// <param name="list">Target SharePoint List</param>
        /// <param name="spQuery">Query used for retrieving data</param>
        /// <returns>true if the resultset of query contains any item; otherwise, false.</returns>
        public static bool Any(this SPList list, SPQuery spQuery)
        {
            var query = new Query(spQuery, list);
            return query.Any();
        }

        /// <summary>
        /// Determines whether the default list view contains any item.
        /// </summary>
        /// <param name="list">Target SharePoint List</param>
        /// <param name="view">Target View</param>
        /// <returns>true if the view contains any item; otherwise, false.</returns>
        public static bool Any(this SPList list, SPView view)
        {
            var queryStr = string.Format("<View>{0}{1}<QueryOptions><ViewAttributes Scope=\"{2}\" /><RowLimit>{3}</RowLimit></QueryOptions></View>",
                                         view.ViewFields.SchemaXml, view.Query, view.Scope, view.RowLimit);
            var query = new Query(queryStr, list);
            return query.Any();
        }

        #endregion

        #region RandomId

        public static int RandomId(this SPList list)
        {
            return list.RandomId(list.DefaultView);
        }

        public static int RandomId(this SPList list, SPView view)
        {
            var queryStr = string.Format("<View>{0}{1}<QueryOptions><ViewAttributes Scope=\"{2}\" /><RowLimit>{3}</RowLimit></QueryOptions></View>",
                                         view.ViewFields.SchemaXml, view.Query, view.Scope, view.RowLimit);
            var query = new Query(queryStr, list);
            return query.RandomId();
        }

        #endregion

        #region ToDataTable

        public static DataTable ToDataTable(this SPList list)
        {
            return list.ToDataTable(list.DefaultView);
        }

        public static DataTable ToDataTable(this SPList list, SPView view)
        {
            return list.ToDataTable(view, 0, view.RowLimit);
        }

        public static DataTable ToDataTable(this SPList list, SPQuery spQuery)
        {
            return list.ToDataTable(spQuery, 0, spQuery.RowLimit);
        }

        public static DataTable ToDataTable(this SPList list, SPQuery spQuery, uint pageIndex, uint pageSize)
        {
            var queryStr = string.Format("<View>{0}{1}<QueryOptions><ViewAttributes {2} /><RowLimit>{3}</RowLimit></QueryOptions></View>",
                                         spQuery.ViewFields, spQuery.Query, spQuery.ViewAttributes, spQuery.RowLimit);
            var query = new Query(queryStr, list) { PageSize = pageSize, PageIndex = pageIndex };
            return query.ToDataTable();
        }

        public static DataTable ToDataTable(this SPList list, SPView view, uint pageIndex, uint pageSize)
        {
            var queryStr = string.Format("<View>{0}{1}<QueryOptions><ViewAttributes Scope=\"{2}\" /><RowLimit>{3}</RowLimit></QueryOptions></View>",
                                         view.ViewFields.SchemaXml, view.Query, view.Scope, view.RowLimit);
            var query = new Query(queryStr, list);
            return query.ToDataTable();
        }

        #endregion

        #region ToEntityList

        public static IEnumerable<T> ToEntityList<T>(this SPList list, SPView view, uint pageIndex, uint pageSize)
        {
            var queryStr = string.Format("<View>{0}{1}<QueryOptions><ViewAttributes Scope=\"{2}\" /><RowLimit>{3}</RowLimit></QueryOptions></View>",
                                         view.ViewFields.SchemaXml, view.Query, view.Scope, view.RowLimit);
            var query = new Query(queryStr, list) { PageSize = pageSize, PageIndex = pageIndex };
            return query.ToEntityList<T>();
        }

        public static IEnumerable<T> ToEntityList<T>(this SPList list, SPQuery spQuery, uint pageIndex, uint pageSize)
        {
            var queryStr = string.Format("<View>{0}{1}<QueryOptions><ViewAttributes {2} /><RowLimit>{3}</RowLimit></QueryOptions></View>",
                                         spQuery.ViewFields, spQuery.Query, spQuery.ViewAttributes, spQuery.RowLimit);
            var query = new Query(queryStr, list) { PageSize = pageSize, PageIndex = pageIndex };
            return query.ToEntityList<T>();
        }

        public static IEnumerable<T> ToEntityList<T>(this SPList list, SPQuery spQuery)
        {
            return list.ToEntityList<T>(spQuery, 0, spQuery.RowLimit);
        }

        public static IEnumerable<T> ToEntityList<T>(this SPList list, SPView view)
        {
            return list.ToEntityList<T>(view, 0, view.RowLimit);
        }

        #endregion

        #region Internal
        internal static IEnumerable<FieldInfo> GetFields(SPList list, SPQuery query)
        {
            var str = query.ViewFields;
            var allFields = list.GetFields();
            if (string.IsNullOrEmpty(str))
                return allFields;
            var xElement = XElement.Parse(str);
            var fieldsByName = xElement.Elements()
                .Where(e => e.Attribute("Name") != null)
                .Select(x => x.Attribute("Name").Value);
            var fiedlsById = xElement.Elements()
                .Where(e => e.Attribute("ID") != null && e.Attribute("Name") == null)
                .Select(x => new Guid(x.Attribute("ID").Value))
                .ToList();
            return allFields
                .Where(f => fiedlsById.Contains(f.Id) || fieldsByName.Contains(f.InternalName));
        }

        internal static IEnumerable<FieldInfo> GetFields(this SPList list)
        {
            return list.Fields
                .Cast<SPField>()
                .Select(f => new FieldInfo(f))
                .Where(f => !string.IsNullOrEmpty(f.ColumnName))
                .OrderBy(f => f.Title)
                .ToList();
        }

        internal static string ValueFormat(this SPFieldType fieldType)
        {
            switch (fieldType)
            {
                case SPFieldType.Invalid:
                case SPFieldType.Text:
                case SPFieldType.Note:
                case SPFieldType.DateTime:
                case SPFieldType.Choice:
                case SPFieldType.URL:
                case SPFieldType.ContentTypeId:
                    return "'{0}'";
                case SPFieldType.Integer:
                case SPFieldType.Counter:
                case SPFieldType.Boolean:
                case SPFieldType.Number:
                case SPFieldType.Currency:
                case SPFieldType.Attachments:
                case SPFieldType.MaxItems:
                case SPFieldType.ModStat:
                    return "{0}";
                case SPFieldType.Lookup:
                case SPFieldType.Computed:
                case SPFieldType.MultiChoice:
                case SPFieldType.Calculated:
                case SPFieldType.User:
                    return "'{0}'";
            }
            return "{0}";
        }
        #endregion
    }
}
